using System;
using System.Collections.Generic;

namespace RayDen.RayEngine.Core.Types
{
    public abstract class BaseBuffer<TArg, TRes>
    {
        public TArg[] args;
        public TRes[] results;
        internal int currentFreeElementIndex;
        internal int size;
        protected readonly Stack<int> userData;


        public int ElementsInitialized {
            get { return currentFreeElementIndex; }
        }


        protected BaseBuffer(int size)
        {
            currentFreeElementIndex = 0;
            this.args = new TArg[size];
            this.results = new TRes[size];
            this.size = size;
            this.userData = new Stack<int>();
        }

        public abstract bool IsHit(int index);

        public virtual void Reset() {
            this.currentFreeElementIndex = 0;
        }

        public int Size {
            get { return this.size; }
        }

        public bool IsFull {
            get {
                return currentFreeElementIndex >= size - 1;
            }
        }

        public int GetRayCount() {
            return currentFreeElementIndex;
        }



        public int Add(TArg r) {
#if VERBOSE
            try {
#endif
            if (currentFreeElementIndex >= Size) {
                throw new ArgumentException("Ray Buffer is full");
            }
            args[currentFreeElementIndex] = r;
#if VERBOSE
            }
            catch(Exception ex) {
                Console.WriteLine(ex.Message);
            }
#endif
            return currentFreeElementIndex++;
        }
        public int Add(ref TArg r) {
            if (currentFreeElementIndex >= Size) {
                throw new ArgumentException();
            }
            args[currentFreeElementIndex] = r;
            return currentFreeElementIndex++;
        }
        public int Reserve() {
            return currentFreeElementIndex++;
        }

        public void Flush() {
#if VERBOSE
            GC.Collect();
#endif
        }

        public int LeftSpace() {
            return (size - 1) - currentFreeElementIndex;
        }

        public void PushUserData(int data) {
            userData.Push(data);
        }

        public int GetUserData() {
            return userData.Peek();
        }

        public int PopUserData() {
            int data = userData.Pop();
            return data;
        }

        public int GetUserDataCount() {
            return userData.Count;
        }

        public void ResetUserData() {
            userData.Clear();
        }
    }
}